<?php

/* 
 * ScopeOut behavior for cakePHP 
 * comments, bug reports are welcome skie AT mail DOT ru 
 * @author Yevgeny Tomenko aka SkieDr 
 * @version 1.0.0.5
 
 configuration is
 
 1)  
	var $actsAs = array(
		'ScopeOut' => array(
			'Notdone' => array('conditions' => array('Todo.done' => 0)),
			'Done' => array('conditions' => array('Todo.done' => 1)),
		),
	);
  
 */ 

class ScopeOutBehavior extends ModelBehavior {

	var $settings = null;

	var $mapMethods = array();
	
	function setup(&$model, $config = array()) {
		if (is_array($config)) {
			foreach ($config as $map => $cond) {
				$this->__addMaping($model, $map, $cond);
			}
		}
	}

	function __addMaping(&$model, $map, $config) {
		$settings=array();
		$this->mapMethods['/find'.$map.'/'] = 'findScoped';
		$this->mapMethods['/findAll'.$map.'/'] = 'findAllScoped';
		$settings['mapMethods']['find'.$map] = $config;
		$settings['mapMethods']['findAll'.$map] = $config;
		$this->settings[$model->name] = Set::merge($this->settings[$model->name],$settings);
	}	
		
	
	/**
	 * Set scope mapping
	 *
	 * @param AppModel $model
	 * @return boolean True to continue, false to abort the save
	 */ 
	function findScoped(&$model, $method, $conditions = null, $fields = null, $order = null, $recursive = null) {
		return $this->__scoped($model, 'find', $method, $conditions, $fields, $order, null, 1, $recursive);
	} 

	function findAllScoped(&$model, $method, $conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
		//$data=compact('method', 'conditions', 'fields', 'order', 'limit', 'page', 'recursive');
		return $this->__scoped($model, 'findAll', $method, $conditions, $fields, $order, $limit, $page, $recursive);
	}

	function __scoped(&$model, $type, $method, $conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
		if ($type=='find') {
			if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $model->__findMethods))) {
				$typeN = 'first';
				$query = compact('conditions', 'fields', 'order', 'recursive', 'limit', 'page');
			} else {
				$typeN = $conditions;
				$query = $fields;
			}
		} elseif ($type='findAll') {
			$typeN = 'all';
			$query = compact('conditions', 'fields', 'order', 'recursive', 'limit', 'page');
		}
			
		list($cond, $findBy)=$this->__getMap($model, $method, $type);
		if ($method=='findAllRestrictDesc') {
//			debug($query);
//			debug($cond);
		}
		$query=$this->__combine($query,$cond);
		//$query=array_merge_recursive($query,$cond);
		if ($method=='findAllRestrictDesc') {
//			debug($query);
		}
		extract($query);
		if ($findBy) {
			$conds=array();
			$conds[$model->name.'.'.$findBy]=$conditions;
			$conditions=$conds;
		} else {
			if (empty($conditions)) {
				$conditions=array();
			} elseif (!is_array($conditions)) {
				$conditions=array($conditions);
			}
		}
		if ($findBy) {
			return $model->{$type."By$findBy"}($conditions, $fields, $order, $limit, $page, $recursive);
		} else {
			 if (isset($typeN)) {
				$query['conditions']=$conditions;
				return $model->find($typeN, $query);
			
			 } else {
				return $model->{"$type"}($conditions, $fields, $order, $limit, $page, $recursive);
			}
		}
	} 

	function __combine($a, $b) {
		$keys = array();
		if (is_array($a)) { 
			$keys=am($keys,array_keys($a));
		}
		if (is_array($b)) { 
			$keys=am($keys,array_keys($b));
		}
		foreach ($keys as $string) {
			if (empty($a[$string]) && isset($b[$string])) {
				$a[$string]=$b[$string];
			}
		}
		return $a;
	}
	
	
	function __getMap(&$model, $method, $localMethod) {
		$map=$this->settings[$model->name]['mapMethods'];
		$name=substr($method,strlen($localMethod));
		if (isset($map[$method])) {
			return array($map[$method], false);
		}
		$mapKeys=array_keys($map);
		$mapMethods = array_map('strtolower', $mapKeys);
		$count = count($mapMethods);
		$lmethod=low($method);
		for ($i = 0; $i < $count; $i++) {
			if (substr($lmethod,0,strlen($mapMethods[$i])+2)==$mapMethods[$i].'by') {
				$fieldName=Inflector::underscore(Inflector::variable(substr($method,strlen($mapMethods[$i])+2)));
				return array($map[$mapKeys[$i]], $fieldName);
			}
		}
		trigger_error(sprintf(__('(ScopeOut::getMap) Undefined scope %s in %sScoped is used.', true), $name, $localMethod), E_USER_ERROR);
	}	
	
}
?>